<?php
declare (strict_types = 1);
namespace app\common;

use think\Validate;

class ValidateExt extends Validate
{
    protected $field_determiner = "";

    public function __construct()
    {
        $appendTypeMsg = [
            'notEmpty' => ':attribute cannot be an empty string',
            'id' => ':attribute must be positive integer',
            'ids' => ':attribute must be id list',
            'money' => ':attribute must be money number',
            'dateGT' => ':attribute must greater than :rule',
            'dateLT' => ':attribute must less than :rule',
            'username' => ':attribute not a valid username',
            'password' => ':attribute not a valid password',
            'wechatAppId' => ':attribute not a valid wechat appid',
            'wechatAppSecret' => ':attribute not a valid wechat appsecret',
            'fullMobile' => ':attribute not a valid full mobile number',
        ];
        $this->typeMsg = array_merge($this->typeMsg, $appendTypeMsg);
        parent::__construct();
    }

    public function setFieldDeterminer($determiner = '')
    {
        $this->field_determiner = $determiner;
    }

    protected function checkItem(string $field, $value, $rules, $data, string $title = '', array $msg = [])
    {
        $lang_key = "FIELD_{$this->field_determiner}{$field}";
        if ($this->lang->has($lang_key)) {
            $title = $this->lang->get($lang_key);
        }

        if (isset($this->remove[$field]) && true === $this->remove[$field] && empty($this->append[$field])) {
            // 字段已经移除 无需验证
            return true;
        }

        // 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...]
        if (is_string($rules)) {
            $rules = explode('|', $rules);
        }

        if (isset($this->append[$field])) {
            // 追加额外的验证规则
            $rules = array_unique(array_merge($rules, $this->append[$field]), SORT_REGULAR);
            unset($this->append[$field]);
        }

        if (empty($rules)) {
            return true;
        }

        $i = 0;
        foreach ($rules as $key => $rule) {
            if ($rule instanceof Closure) {
                $result = call_user_func_array($rule, [$value, $data]);
                $info = is_numeric($key) ? '' : $key;
            } else {
                // 判断验证类型
                [$type, $rule, $info] = $this->getValidateType($key, $rule);

                if (isset($this->append[$field]) && in_array($info, $this->append[$field])) {
                } elseif (isset($this->remove[$field]) && in_array($info, $this->remove[$field])) {
                    // 规则已经移除
                    $i++;
                    continue;
                }

                if (isset($this->type[$type])) {
                    $result = call_user_func_array($this->type[$type], [$value, $rule, $data, $field, $title]);
                } elseif ('must' == $info || 'notEmpty' == $info || 0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
                    $result = call_user_func_array([$this, $type], [$value, $rule, $data, $field, $title]);
                } else {
                    $result = true;
                }
            }

            if (false === $result) {
                // 验证失败 返回错误信息
                if (!empty($msg[$i])) {
                    $message = $msg[$i];
                    if (is_string($message) && strpos($message, '{%') === 0) {
                        $message = $this->lang->get(substr($message, 2, -1));
                    }
                } else {
                    $message = $this->getRuleMsg($field, $title, $info, $rule);
                }

                return $message;
            } elseif (true !== $result) {
                // 返回自定义错误信息
                if (is_string($result) && false !== strpos($result, ':')) {
                    $result = str_replace(':attribute', $title, $result);

                    if (strpos($result, ':rule') && is_scalar($rule)) {
                        $result = str_replace(':rule', (string) $rule, $result);
                    }
                }

                return $result;
            }
            $i++;
        }

        return $result ?? true;
    }

    /**
     * 默认情况：当字段值是null或者空字符串时，除了require规则（包括require开头及must），其他规则直接通过，不再调用验证函数
     * notEmpty规则不管字段值如何，都调用此函数验证，其效果是：该字段要么不传，传了就不能为空字符串
     * 虽然null也会通过该函数的验证，但是字段不传时，通过request获取的参数数组是不包含该字段的
     */
    protected function notEmpty($value)
    {
        return $value !== '';
    }

    protected function id($value)
    {
        if (!is_string($value)) {
            return false;
        }
        return $this->filter($value, FILTER_VALIDATE_INT) && $value >= 0;
    }

    protected function ids($value)
    {
        if (is_string($value)) {
            $value = explode(',', $value);
        }
        if (!is_array($value)) {
            return false;
        }
        foreach ($value as $v) {
            if (!$this->id($v)) {
                return false;
            }
        }
        return true;
    }

    protected function money($value)
    {
        if (!is_string($value)) {
            return false;
        }
        return preg_match('/^[0-9]{1,8}(.[0-9]{1,2})?$/', $value) ? true : false;
    }

    protected function dateGT($value, $rule, $data)
    {
        if (!is_string($value)) {
            return false;
        }
        return strtotime($value) > strtotime($data[$rule]);
    }

    protected function dateLT($value, $rule, $data)
    {
        if (!is_string($value)) {
            return false;
        }
        return strtotime($value) < strtotime($data[$rule]);
    }

    protected function username($value)
    {
        //字母开头，且只可包含字母、数字、下划线（_），长度5-20位。
        if (!is_string($value)) {
            return false;
        }
        if (!preg_match('/^[a-zA-Z][a-zA-Z0-9_]{4,19}$/', $value)) {
            return false;
        }
        return true;
    }

    protected function password($value)
    {
        //必须包含大写字母、小写字母、数字，且长度8-20位。
        if (!is_string($value)) {
            return false;
        }
        if (!preg_match('/^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,20}$/', $value)) {
            return false;
        }
        return true;
    }

    protected function wechatAppId($value)
    {
        if (!is_string($value)) {
            return false;
        }
        if (!preg_match('/^wx[0-9a-f]{16}$/', $value)) {
            return false;
        }
        return true;
    }

    protected function wechatAppSecret($value)
    {
        if (!is_string($value)) {
            return false;
        }
        if (!preg_match('/^[0-9a-f]{32}$/', $value)) {
            return false;
        }
        return true;
    }

    protected function fullMobile($value)
    {
        if (!is_string($value)) {
            return false;
        }
        return preg_match('/^\d{1,5}[\-](?:(?<=86[\-])1[3-9]\d{9}|(?<!86[\-])\d{5,11})$/', $value) ? true : false;
    }
}
